﻿using System;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.Routing;

namespace ShowPin.MvcPaging
{
    /// <summary>
    /// 分页UI
    /// </summary>
    public class Pager : IHtmlString
    {
        private readonly HtmlHelper _htmlHelper;
        private readonly int _pageSize;
        private readonly int _currentPage;
        private readonly int _totalItemCount;
        private readonly PagerOptions _pagerOptions;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <param name="pageSize">每页显示数据量</param>
        /// <param name="currentPage">当前页</param>
        /// <param name="totalItemCount">总数据量</param>
        public Pager(HtmlHelper htmlHelper, int pageSize, int currentPage, int totalItemCount)
        {
            _htmlHelper = htmlHelper;
            _pageSize = pageSize;
            _currentPage = currentPage;
            _totalItemCount = totalItemCount;
            _pagerOptions = new PagerOptions();
        }

        /// <summary>
        /// 设置额外选项
        /// </summary>
        /// <param name="buildOptions">构造分页模型的委托</param>
        /// <returns></returns>
        public Pager Options(Action<PagerOptionsBuilder> buildOptions)
        {
            buildOptions(new PagerOptionsBuilder(_pagerOptions));
            return this;
        }

        /// <summary>
        /// 构造分页模型
        /// </summary>
        /// <param name="generateUrl">生成 URL 的委托</param>
        /// <returns></returns>
        public PaginationModel BuildPaginationModel(Func<int, string> generateUrl)
        {
            var model = new PaginationModel();
            if (_pageSize <= 0)
            {
                return model;
            }
            //总页数
            var totalPage = (int)Math.Ceiling((double)_totalItemCount / _pageSize);
            if (totalPage <= 1 || totalPage < _currentPage) return model;
            int leftPageNum;//左边界
            int pageNumber = _pagerOptions.PageNumber;
            if (totalPage > pageNumber)
            {
                int step;//偏移量
                if (pageNumber % 2 == 0)//如果是偶数 则偏移量取 pageNum / 2 + 1
                {
                    step = Convert.ToInt32(((double)pageNumber / 2).ToString("f0")) + 1;
                }
                else
                {
                    step = Convert.ToInt32(((double)pageNumber / 2).ToString("f0"));
                }
                leftPageNum = _currentPage - step < 1 ? 1 : _currentPage - step + 1;
                if (leftPageNum + pageNumber - 1 > totalPage)
                {
                    leftPageNum = totalPage - pageNumber + 1;
                }
            }
            else
            {
                leftPageNum = 1;
                pageNumber = totalPage;
            }

            var sourceList = Enumerable.Range(leftPageNum, pageNumber);

            model.PaginationLinks.Add(_currentPage > 1 ?
                new PaginationLink
                {
                    IsEnabled = true,
                    DisplayText = "«",
                    PageIndex = _currentPage - 1,
                    Url = generateUrl(_currentPage - 1)
                }
                :
                new PaginationLink
                {
                    IsEnabled = false,
                    DisplayText = "«"
                });
            foreach (int index in sourceList)
            {
                model.PaginationLinks.Add(_currentPage == index ?
                    new PaginationLink
                    {
                        IsEnabled = true,
                        PageIndex = index,
                        IsCurrent = true,
                        DisplayText = index.ToString(CultureInfo.InvariantCulture)
                    }
                    : new PaginationLink
                    {
                        IsEnabled = true,
                        PageIndex = index,
                        DisplayText = index.ToString(CultureInfo.InvariantCulture),
                        Url = generateUrl(index)
                    });
            }
            model.PaginationLinks.Add(_currentPage < totalPage ?
                new PaginationLink
                {
                    IsEnabled = true,
                    PageIndex = _currentPage + 1,
                    DisplayText = "»",
                    Url = generateUrl(_currentPage + 1)
                }
                :
                new PaginationLink
                {
                    IsEnabled = false,
                    DisplayText = "»"
                });

            // AjaxOptions
            if (_pagerOptions.AjaxOptions != null)
            {
                model.AjaxOptions = _pagerOptions.AjaxOptions;
            }
            //PagerOptions
            model.Options = _pagerOptions;
            return model;
        }

        /// <summary>
        /// 返回 HTML 编码的字符串
        /// </summary>
        /// <returns></returns>
        public string ToHtmlString()
        {
            var model = BuildPaginationModel(GeneratePageUrl);
            if (!String.IsNullOrEmpty(_pagerOptions.DisplayTemplate))
            {
                var templatePath = string.Format("DisplayTemplates/{0}", _pagerOptions.DisplayTemplate);
                return _htmlHelper.Partial(templatePath, model).ToHtmlString();
            }
            if (model.PaginationLinks.Count <= 0)
            {
                return "";
            }

            var ul = new TagBuilder("ul");
            ul.MergeAttributes(_pagerOptions.HtmlAttributes);
            foreach (var paginationLink in model.PaginationLinks)
            {
                var li = new TagBuilder("li");
                if (!paginationLink.IsEnabled)
                {
                    li.MergeAttribute("class", "disabled");
                }
                if (paginationLink.IsCurrent)
                {
                    li.MergeAttribute("class", "active");
                }
                var a = new TagBuilder("a");
                a.MergeAttribute("href",
                    string.IsNullOrWhiteSpace(paginationLink.Url) ? "javascript:" : paginationLink.Url);
                // Ajax support
                if (_pagerOptions.AjaxOptions != null)
                {
                    foreach (var ajaxOption in _pagerOptions.AjaxOptions.ToUnobtrusiveHtmlAttributes())
                    {
                        a.MergeAttribute(ajaxOption.Key, ajaxOption.Value.ToString(), true);
                    }
                }
                a.SetInnerText(paginationLink.DisplayText);
                li.InnerHtml += a.ToString();
                ul.InnerHtml += li.ToString();
            }
            return ul.ToString();
        }

        /// <summary>
        /// 获取 URL
        /// </summary>
        /// <param name="pageNumber">页码</param>
        /// <returns></returns>
        private string GeneratePageUrl(int pageNumber)
        {
            var viewContext = _htmlHelper.ViewContext;
            var routeDataValues = viewContext.RequestContext.RouteData.Values;
            RouteValueDictionary pageLinkValueDictionary;
            // Avoid canonical errors when page count is equal to 1.
            if (pageNumber == 1 /*&& !this.pagerOptions.AlwaysAddFirstPageNumber*/)
            {
                pageLinkValueDictionary = new RouteValueDictionary(_pagerOptions.RouteValues);
                if (routeDataValues.ContainsKey(_pagerOptions.PageRouteValueKey))
                {
                    routeDataValues.Remove(_pagerOptions.PageRouteValueKey);
                }
            }
            else
            {
                pageLinkValueDictionary = new RouteValueDictionary(_pagerOptions.RouteValues) { { _pagerOptions.PageRouteValueKey, pageNumber } };
            }
            VirtualPathData virtualPathForArea;
            if (string.IsNullOrWhiteSpace(_pagerOptions.RouteName))
            {
                // To be sure we get the right route, ensure the controller and action are specified.
                if (!pageLinkValueDictionary.ContainsKey("controller") && routeDataValues.ContainsKey("controller"))
                {
                    pageLinkValueDictionary.Add("controller", routeDataValues["controller"]);
                }
                if (!pageLinkValueDictionary.ContainsKey("action") && routeDataValues.ContainsKey("action"))
                {
                    pageLinkValueDictionary.Add("action", routeDataValues["action"]);
                }
                // Fix the dictionary if there are arrays in it.
                pageLinkValueDictionary = pageLinkValueDictionary.FixListRouteDataValues();
                // 'Render' virtual path.
                virtualPathForArea = RouteTable.Routes.GetVirtualPathForArea(viewContext.RequestContext, pageLinkValueDictionary);
            }
            else
            {
                // Fix the dictionary if there are arrays in it.
                pageLinkValueDictionary = pageLinkValueDictionary.FixListRouteDataValues();
                // 'Render' virtual path.
                virtualPathForArea = RouteTable.Routes.GetVirtualPathForArea(viewContext.RequestContext, _pagerOptions.RouteName, pageLinkValueDictionary);
            }

            return virtualPathForArea == null ? null : virtualPathForArea.VirtualPath;
        }
    }
}
